Práctica Final by Javier Ramos¶

In [1]:
import pandas as pd
import numpy as np
from numpy.linalg import eigh
from datetime import datetime
import seaborn as sns
sns.set(color_codes=True)
import matplotlib.pyplot as plt
%matplotlib inline
!pip install category_encoders
import category_encoders as ce
from scipy.stats import chi2_contingency
import sklearn as sklearn
from sklearn import metrics
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder, OrdinalEncoder
from sklearn.covariance import EllipticEnvelope
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA, FactorAnalysis, KernelPCA, SparsePCA
from sklearn.manifold import TSNE, Isomap
from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier, IsolationForest
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score
!pip install umap-learn
from umap import UMAP
Requirement already satisfied: category_encoders in /usr/local/lib/python3.10/dist-packages (2.6.3)
Requirement already satisfied: numpy>=1.14.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.23.5)
Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.2.2)
Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.11.4)
Requirement already satisfied: statsmodels>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (0.14.1)
Requirement already satisfied: pandas>=1.0.5 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.5.3)
Requirement already satisfied: patsy>=0.5.1 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (0.5.6)
Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.0.5->category_encoders) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.0.5->category_encoders) (2023.3.post1)
Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from patsy>=0.5.1->category_encoders) (1.16.0)
Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->category_encoders) (1.3.2)
Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->category_encoders) (3.2.0)
Requirement already satisfied: packaging>=21.3 in /usr/local/lib/python3.10/dist-packages (from statsmodels>=0.9.0->category_encoders) (23.2)
Requirement already satisfied: umap-learn in /usr/local/lib/python3.10/dist-packages (0.5.5)
Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.23.5)
Requirement already satisfied: scipy>=1.3.1 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.11.4)
Requirement already satisfied: scikit-learn>=0.22 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.2.2)
Requirement already satisfied: numba>=0.51.2 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (0.58.1)
Requirement already satisfied: pynndescent>=0.5 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (0.5.11)
Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from umap-learn) (4.66.1)
Requirement already satisfied: llvmlite<0.42,>=0.41.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba>=0.51.2->umap-learn) (0.41.1)
Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.10/dist-packages (from pynndescent>=0.5->umap-learn) (1.3.2)
Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.22->umap-learn) (3.2.0)

Ejercicio 1.¶

Una vez hemos llamado a las potenciales librerías que podemos utilizar, procederemos a importar el DS: "siniestros_tiny". Esta base de datos: "Transport Canada’s National Collision Database (NCDB)" contiene datos sobre todas las colisiones de vehículos de motor notificables en Canadá que las provincias y territorios proporcionan cada año.

In [2]:
x = pd.read_csv("/siniestros_tiny.csv")
x.head(5)
Out[2]:
C_YEAR C_MNTH C_WDAY C_HOUR C_SEV C_VEHS C_CONF C_RCFG C_WTHR C_RSUR ... V_ID V_TYPE V_YEAR P_ID P_SEX P_AGE P_PSN P_ISEV P_SAFE P_USER
0 2001 5 5 16 2 02 33 02 1 1 ... 01 01 1986 02 M 15 13 1 UU 2
1 2008 10 3 13 2 02 21 02 1 1 ... 02 05 2002 02 M 04 23 1 02 2
2 2009 8 7 09 2 02 36 02 1 1 ... 2 01 2004 02 F 17 21 2 02 2
3 2003 4 5 18 2 2 UU UU U U ... 2 01 1996 03 F 02 21 2 02 2
4 2014 9 1 07 2 03 21 01 1 1 ... 2 01 2001 01 F 36 11 2 02 1

5 rows × 22 columns

Una vez importados los datos, limpiaremos los nombres de las columnas. Pondremos todas las letras de los nombres de las columnas en minúscula, utilizando la función map() de esta manera será más fácil operar con ellos.

In [3]:
x.columns = map(str.lower,x.columns)
x.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20000 entries, 0 to 19999
Data columns (total 22 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   c_year  20000 non-null  int64 
 1   c_mnth  20000 non-null  object
 2   c_wday  20000 non-null  object
 3   c_hour  20000 non-null  object
 4   c_sev   20000 non-null  int64 
 5   c_vehs  20000 non-null  object
 6   c_conf  20000 non-null  object
 7   c_rcfg  20000 non-null  object
 8   c_wthr  20000 non-null  object
 9   c_rsur  20000 non-null  object
 10  c_raln  20000 non-null  object
 11  c_traf  20000 non-null  object
 12  v_id    20000 non-null  object
 13  v_type  20000 non-null  object
 14  v_year  20000 non-null  object
 15  p_id    20000 non-null  object
 16  p_sex   20000 non-null  object
 17  p_age   20000 non-null  object
 18  p_psn   20000 non-null  object
 19  p_isev  20000 non-null  object
 20  p_safe  20000 non-null  object
 21  p_user  20000 non-null  object
dtypes: int64(2), object(20)
memory usage: 3.4+ MB

Para esta primera parte realizaremos un análisis descriptivo enfocado en la relación de las variables con la severidad de las colisiones, llevaremos esto a cabo mediante un correlograma.

In [4]:
mcorr = x.corr()

sns.set(style="white")
plt.figure(figsize=(4, 4))
sns.heatmap(mcorr, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.subplots_adjust(bottom=0.2)
plt.title('Matriz de Correlaciones')
plt.show()
<ipython-input-4-1f336dded474>:1: FutureWarning: The default value of numeric_only in DataFrame.corr is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.
  mcorr = x.corr()

Como podemos observar, realizar la matriz de correlaciones sin un análisis exploratorio de los datos no tiene sentido. Sin embargo, para continuar con la práctica comprobaremos el tipo de datos de las diferentes columnas, debido a que el hecho de que solo aparezcan dos de las variables, estará relacionado con el tipo de datos de las columnas, para ello utilizaremos la función info().

In [5]:
x.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20000 entries, 0 to 19999
Data columns (total 22 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   c_year  20000 non-null  int64 
 1   c_mnth  20000 non-null  object
 2   c_wday  20000 non-null  object
 3   c_hour  20000 non-null  object
 4   c_sev   20000 non-null  int64 
 5   c_vehs  20000 non-null  object
 6   c_conf  20000 non-null  object
 7   c_rcfg  20000 non-null  object
 8   c_wthr  20000 non-null  object
 9   c_rsur  20000 non-null  object
 10  c_raln  20000 non-null  object
 11  c_traf  20000 non-null  object
 12  v_id    20000 non-null  object
 13  v_type  20000 non-null  object
 14  v_year  20000 non-null  object
 15  p_id    20000 non-null  object
 16  p_sex   20000 non-null  object
 17  p_age   20000 non-null  object
 18  p_psn   20000 non-null  object
 19  p_isev  20000 non-null  object
 20  p_safe  20000 non-null  object
 21  p_user  20000 non-null  object
dtypes: int64(2), object(20)
memory usage: 3.4+ MB

Como podemos ver, se cumple la hipótesis de que el principal problema del correlograma es el tipo de datos de las columnas. Las dos variables que aparecen en el correlograma superior son: "c_year" y "c_sev" que coinciden con las columnas que tienen datos del tipo int64. Es por esto por lo que tendremos que seleccionar todas aquellas columnas que sean del tipo object y recategorizarlas mediante el OneHotEncoder.

In [6]:
catcols = list(x.select_dtypes("object").columns)
list_other = list(set(x.columns)-set(catcols))
ohe = ce.OneHotEncoder(cols=catcols)
model = ohe.fit(x)
model
Out[6]:
OneHotEncoder(cols=['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
                    'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
                    'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
                    'p_safe', 'p_user'])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
OneHotEncoder(cols=['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
                    'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
                    'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
                    'p_safe', 'p_user'])
In [7]:
xtrans = model.transform(x)
xtrans.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20000 entries, 0 to 19999
Columns: 441 entries, c_year to p_user_6
dtypes: int64(441)
memory usage: 67.3 MB

Como se puede observar mediante el uso de describe() el OneHotEncoder no nos sería útil en este caso, ya que mediante el uso de este método, al haber muchas diferentes categorías dentro de las variables, nos genera muchas columnas, en este caso 441, por lo que al realizar un correlograma aunque lo hagamos de unas dimensiones muy grandes, no se podrá apreciar el heatmap debido a que los números se superpodrán unos a otros y no se entenderá nada.

Es por ello por lo que he considerado que sería más interesante aplicar un OrdinalEncoder, que resulta útil cuando tenemos datos ordinales, es decir, aquellos en los que se puede establecer un orden entre sus categorías.

In [8]:
columnas_categoricas = ['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
                    'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
                    'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
                    'p_safe', 'p_user']

xcat = x[columnas_categoricas]

oe = OrdinalEncoder()
catencod = pd.DataFrame(oe.fit_transform(xcat), columns=columnas_categoricas)
x[columnas_categoricas] = catencod
x[columnas_categoricas] = x[columnas_categoricas].astype('int64')
x.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20000 entries, 0 to 19999
Data columns (total 22 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   c_year  20000 non-null  int64
 1   c_mnth  20000 non-null  int64
 2   c_wday  20000 non-null  int64
 3   c_hour  20000 non-null  int64
 4   c_sev   20000 non-null  int64
 5   c_vehs  20000 non-null  int64
 6   c_conf  20000 non-null  int64
 7   c_rcfg  20000 non-null  int64
 8   c_wthr  20000 non-null  int64
 9   c_rsur  20000 non-null  int64
 10  c_raln  20000 non-null  int64
 11  c_traf  20000 non-null  int64
 12  v_id    20000 non-null  int64
 13  v_type  20000 non-null  int64
 14  v_year  20000 non-null  int64
 15  p_id    20000 non-null  int64
 16  p_sex   20000 non-null  int64
 17  p_age   20000 non-null  int64
 18  p_psn   20000 non-null  int64
 19  p_isev  20000 non-null  int64
 20  p_safe  20000 non-null  int64
 21  p_user  20000 non-null  int64
dtypes: int64(22)
memory usage: 3.4 MB

Una vez comprobado que tras la recategorización de las variables a int64 no hemos perdido ningún valor, vamos a estandarizar las variables del conjunto de datos mediante la función StandardScaler.

In [9]:
scaler = StandardScaler()
model_scaled = scaler.fit(x)
xscal = pd.DataFrame(scaler.transform(x), columns=x.columns, index=x.index)
xscal.describe()
Out[9]:
c_year c_mnth c_wday c_hour c_sev c_vehs c_conf c_rcfg c_wthr c_rsur ... v_id v_type v_year p_id p_sex p_age p_psn p_isev p_safe p_user
count 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 ... 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04 2.000000e+04
mean -6.189182e-15 -4.192202e-17 8.792966e-17 -1.293188e-16 -3.101519e-16 1.953993e-17 1.705303e-17 7.709389e-17 8.242296e-17 1.065814e-17 ... 1.669775e-17 -6.394885e-18 3.861800e-16 -4.263256e-18 6.075140e-17 1.209699e-16 -1.421085e-18 -2.842171e-17 -3.055334e-17 5.186962e-17
std 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 ... 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00 1.000025e+00
min -1.523723e+00 -2.018871e+00 -1.560344e+00 -2.621201e+00 -7.720496e+00 -1.037241e+00 -1.779513e+00 -5.861629e-01 -5.028017e-01 -4.590698e-01 ... -7.636587e-01 -3.594213e-01 -4.359065e+00 -3.407777e-01 -9.746319e-01 -1.670050e+00 -4.936135e-01 -9.122278e-01 -9.923017e-01 -5.593176e-01
25% -8.694182e-01 -9.431724e-01 -1.042705e+00 -7.196856e-01 1.295254e-01 -9.306248e-01 -6.552493e-01 -5.861629e-01 -5.028017e-01 -4.590698e-01 ... -7.636587e-01 -3.594213e-01 -7.364778e-01 -3.407777e-01 -9.746319e-01 -7.843190e-01 -4.936135e-01 -9.122278e-01 -5.221280e-01 -5.593176e-01
50% 2.987991e-03 1.325260e-01 -7.428113e-03 4.092062e-02 1.295254e-01 -7.769156e-02 -6.552493e-01 -2.416550e-01 -5.028017e-01 -4.590698e-01 ... -6.238572e-01 -3.594213e-01 -3.533191e-02 -3.407777e-01 4.828706e-01 -1.938317e-01 -4.936135e-01 3.275490e-01 -5.221280e-01 -5.593176e-01
75% 8.753941e-01 9.392999e-01 1.027849e+00 6.113753e-01 1.295254e-01 8.818584e-01 1.031147e+00 -2.416550e-01 1.848374e-01 3.599092e-02 ... 4.945548e-01 -3.594213e-01 5.781708e-01 3.489160e-01 4.828706e-01 5.231886e-01 2.095146e-01 3.275490e-01 -5.195420e-02 3.366657e-01
max 1.747800e+00 1.746074e+00 2.063126e+00 1.942436e+00 1.295254e-01 3.547275e+00 1.780656e+00 2.858916e+00 4.998311e+00 4.491538e+00 ... 3.290585e+00 4.275125e+00 1.951248e+00 1.759126e+01 3.397876e+00 2.463361e+00 4.428283e+00 4.046880e+00 2.298915e+00 3.920599e+00

8 rows × 22 columns

In [62]:

Una vez tenemos ya los datos recategorizados y estandarizados, podemos realizar el correlograma ya con todas las variables.

In [10]:
mcorr = xscal.corr()

sns.set(style="white")
plt.figure(figsize=(17, 17))
sns.heatmap(mcorr, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.subplots_adjust(bottom=0.2)
plt.title('Matriz de Correlaciones')
plt.show()

Ejercicio 3.¶

Para este ejercicio, se nos pide explorar distintos métodos para reducir dimensiones hasta explicar el 95% de la varianza. Para ello emplearemos el método del PCA, ya que intentando emplear también el método del Kernel PCA la RAM de mi ordenador colapsa.

In [11]:
pca = PCA()
xpca = pca.fit_transform(xscal)
acvar = pca.explained_variance_ratio_.cumsum()
acvar
Out[11]:
array([0.13334801, 0.22519792, 0.30011693, 0.3722927 , 0.43419729,
       0.48989342, 0.54398442, 0.58952102, 0.63451674, 0.67872925,
       0.7178695 , 0.75620602, 0.7913338 , 0.82431083, 0.85597191,
       0.88733438, 0.9160873 , 0.93857967, 0.95828155, 0.97495829,
       0.99045464, 1.        ])

He realizado un PCA con los datos recategorizados y estandarizados, y como se puede observar en el output superior, tenemos la suma acumulada de las varianzas a medida que vamos añadiendo dimensiones, en este caso vemos como en la dimension nº18 tenemos un 93,85% de la varianza acumulada que ya sería suficiente, pero como nos dicen que tenemos que llegar a un 95%, tendríamos que utilizar 19 dimensiones.

In [12]:
ndim = (acvar >= 0.95).argmax() + 1
plt.figure(figsize=(5, 5))
plt.plot(range(1, len(acvar) + 1), acvar, marker='o')
plt.axvline(ndim, color='red', linestyle='dashed', label=f'{ndim} Dimensiones')
plt.xlabel('Número de Dimensiones')
plt.ylabel('Varianza Acumulativa Explicada')
plt.legend()
plt.show()

En el gráfico superior, tenemos una representación visual de lo que he mencionado antes, en el que se demuestra que son 19 las dimensiones necesarias para llegar al 95% de la varianza explicada.

Ejercicio 4.¶

Para este ejercicio se nos pide utilizar el método de reducción de dimensiones seleccionado para identificar y representar outliers visualmente. Para llevar esto a cabo utilizaremos ahora sí tanto el PCA como el Kernel PCA aplicándoles 2 componentes y utilizando el EllipticEnvelope para la detección de los outliers.

In [13]:
pca2 = PCA(n_components=2)
respca = pca2.fit_transform(xscal)
cp = pd.DataFrame(data=respca, columns=['Componente_1', 'Componente_2'])
outlier_model = EllipticEnvelope(contamination=0.05)
outlier_model.fit(cp)
outliers = outlier_model.predict(cp) == -1

plt.figure(figsize=(7, 7))
sns.scatterplot(x='Componente_1', y='Componente_2', data=cp, hue=outliers, palette={0: 'blue', 1: 'red'}, legend='full')
plt.title('Visualización de Outliers en el Espacio PCA')
plt.show()

En este primer gráfico utilizando el PCA para la detección de outliers, hemos hecho que aquellos valores que sean outliers estén representados en rojo, se puede apreciar como en este caso la elliptic envelope corta casi por el componente 6, haciendo que todos los valores que estén fuera de la curva, sean outliers.

Podemos identificar dentro de todos los outliers que hay como dos grupos de ellos, por un lado, los cercanos al valor 6 del componente 1 y entre los valores -2 y 2 del componente 2. Por el otro lado, los cercanos al valor 6 del componente 2 y entre -2 y 0 del componente 1. Se puede observar como hay muchos más elementos en el primer grupo mencionado y también algún outlier suelto.

In [14]:
kpca = KernelPCA(n_components=2, kernel='rbf')
reskpca = kpca.fit_transform(xscal)
dfkpca = pd.DataFrame(data=reskpca, columns=['Componente_1', 'Componente_2'])
outlier_model = EllipticEnvelope(contamination=0.05)
outlier_model.fit(dfkpca)
outliers = outlier_model.predict(dfkpca) == -1

plt.figure(figsize=(7, 7))
sns.scatterplot(x='Componente_1', y='Componente_2', data=dfkpca, hue=outliers, palette={0: 'blue', 1: 'red'}, legend='full')
plt.title('Visualización de Outliers en el Espacio Kernel PCA')
plt.show()

Empleando el kernel pca, podemos ver como los valores de los ejes están mucho más concentrados alrededor del 0 y hay muchos menos outliers que en el caso anterior.

Se pueden apreciar 3 valores atípicos en la parte inferior del gráfico cercanos al valor -0.2 del componente 1 y con valores negativos rondando el -0.5 del componente 2. En la parte superior del gráfico es donde se encuentra el gran grueso de outliers, cortando la elliptic envelope en un valor cercano al valor 0.2 del componente 2 y entre los valores -0.4 y 0.1 del componente 1.

Ejercicio 5.¶

Para este ejercicio se nos pide aplicar el t-SNE o UMAP para una visualización en 2D y analizar las posibilidades de diferenciar tipos de accidentes, para aplicarlo he seleccionado aquellas variables que considero que pueden ser relevantes a la hora de elegir los tipos de accidente, que son: 'c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', y 'p_isev'.

In [15]:
relcol = ['c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', 'p_isev']
xrel = xscal[relcol]

tsne = TSNE(n_components=2, random_state=42)
reltsne = tsne.fit_transform(xrel)
xtsne = pd.DataFrame(data=reltsne, columns=['Dimensión_1', 'Dimensión_2'])
xtsne['Tipo_Accidente'] = xscal['c_conf']

plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Tipo_Accidente', data=xtsne, palette='viridis', legend='full')
plt.title('Visualización t-SNE de Tipos de Accidentes en 2D')
plt.show()

Como podemos ver en el gráfico superior, a simple vista si se puede diferenciar algún grupo claro, fruto de la combinación de los diferentes tipos de puntos que vemos en la leyenda. Sin embargo, a continuación realizaremos lo mismo pero utilizando el UMAP a ver si hay algún cambio.

In [16]:
umap = UMAP(n_components=2, random_state=42)
relumap = umap.fit_transform(xrel)
xumap = pd.DataFrame(data=relumap, columns=['Dimensión_1', 'Dimensión_2'])
xumap['Tipo_Accidente'] = xscal['c_conf']

plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Tipo_Accidente', data=xumap, palette='viridis', legend='full')
plt.title('Visualización UMAP de Tipos de Accidentes en 2D')
plt.show()
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
  warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
In [ ]:

En este segundo gráfico utilizando el UMAP se puede ver que hay algún outlier que no pertenece a ningún grupo y están solos y luego por la zona central hay como un grupo de datos que no pertenece a ninguno de los grupos de alrededor y están como conexiones. Sin embargo, en el grupo solo podíamos diferenciar 4 o 5 grupos claros, pero mediante el uso del UMAP podemos distinguir más de 8 grupos grandes ya que no hay tantos puntos dispersos en la zona central del gráfico

Ejercicio 6.¶

Para este ejercicio se nos pide aplicar y analizar algoritmos de clustering como K-means, DBSCAN, o Agglomerative Clustering. Para este ejercicio utilizaré las variables seleccionadas en el ejercicio anterior que había considerado relevantes que son: 'c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', y 'p_isev'.

In [17]:
kmeans = KMeans(n_clusters=3, random_state=42)
xumap['KMeans_Clusters'] = kmeans.fit_predict(xumap)

plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='KMeans_Clusters', data=xumap, palette='viridis', legend='full')
plt.title('Resultados de K-means Clustering')
plt.show()
/usr/local/lib/python3.10/dist-packages/sklearn/cluster/_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning
  warnings.warn(

En este primer caso, le hemos aplicado un k-means a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar 3 grandes clusters diferenciados claramente, cada uno en sus respectivas regiones de su color: azul, amarillo y morado.

In [18]:
dbscan = DBSCAN(eps=0.5, min_samples=3)
xumap['DBSCAN_Clusters'] = dbscan.fit_predict(xumap)

plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='DBSCAN_Clusters', data=xumap, palette='viridis')
plt.title('Resultados de DBSCAN Clustering')
plt.show()

En este segundo caso, le hemos aplicado un dbscan a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar que se han formado 6 grupos por las distintas densidades: 0, 20, 40, 60, 80 y 100. Pero no se pueden diferenciar de manera clara los grupos ya que hay muchos puntos que se sobreponen unos de otros. Lo que si que se aprecia es que hay muchos datos pertenecientes al cluster 0, al 20 y al 40. unos cuantos pertenecientes al 60 y muy pocos de los clusteres 80 y 100.

In [19]:
agg_clustering = AgglomerativeClustering(n_clusters=3)
xumap['Agg_Clusters'] = agg_clustering.fit_predict(xumap)

plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Agg_Clusters', data=xumap, palette='viridis', legend='full')
plt.title('Resultados de Agglomerative Clustering')
plt.show()

En este tercer y último caso, le hemos aplicado un agglomerative clustering a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar que también hay 3 clusters como ocurría en el primer caso: azul, amarillo y morado. Sin embargo, a diferencia del clustering realizado por k-medias en el que los grupos se diferenciaban claramente, en este gráfico están los grupos mezclados, sobreponiéndose unos encima de otros y no presenta una estructura clara, por lo que considero que la mejor opción será el uso de un k-means.

Ejercicio 7.¶

Para este último ejercicio se nos pide dividir el conjunto de datos en entrenamiento y test, y usar el t-SNE o UMAP para visualizar y comparar las distribuciones.

In [20]:
xtrain, xtest = train_test_split(xrel, test_size=0.2, random_state=42)

tsne_train = TSNE(n_components=2, random_state=42)
xtsne_trainres = tsne_train.fit_transform(xtrain)
tsne_test = TSNE(n_components=2, random_state=42)
xtsne_testres = tsne_test.fit_transform(xtest)


plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
sns.scatterplot(x=xtsne_trainres[:, 0], y=xtsne_trainres[:, 1], palette='viridis', legend='full')
plt.title('t-SNE en Conjunto de Entrenamiento')

plt.subplot(1, 2, 2)
sns.scatterplot(x=xtsne_testres[:, 0], y=xtsne_testres[:, 1], palette='viridis', legend='full')
plt.title('t-SNE en Conjunto de Prueba')
plt.show()
<ipython-input-20-50cde3800ffc>:11: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
  sns.scatterplot(x=xtsne_trainres[:, 0], y=xtsne_trainres[:, 1], palette='viridis', legend='full')
<ipython-input-20-50cde3800ffc>:15: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
  sns.scatterplot(x=xtsne_testres[:, 0], y=xtsne_testres[:, 1], palette='viridis', legend='full')
In [21]:
umap_train = UMAP(n_components=2, random_state=42)
xumap_trainres = umap_train.fit_transform(xtrain)
umap_test = UMAP(n_components=2, random_state=42)
xumap_testres = umap_test.fit_transform(xtest)


plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
sns.scatterplot(x=xumap_trainres[:, 0], y=xumap_trainres[:, 1], palette='viridis', legend='full')
plt.title('UMAP en Conjunto de Entrenamiento')

plt.subplot(1, 2, 2)
sns.scatterplot(x=xumap_testres[:, 0], y=xumap_testres[:, 1], palette='viridis', legend='full')
plt.title('UMAP en Conjunto de Prueba')
plt.show()
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
  warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
  warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
<ipython-input-21-30c966440ecf>:9: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
  sns.scatterplot(x=xumap_trainres[:, 0], y=xumap_trainres[:, 1], palette='viridis', legend='full')
<ipython-input-21-30c966440ecf>:13: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
  sns.scatterplot(x=xumap_testres[:, 0], y=xumap_testres[:, 1], palette='viridis', legend='full')

En ambos gráficos realizados tanto del tsne como del UMAP, en el conjunto de prueba (test) se pueden distinguir unos pocos grupos claramente, ya que a pesar de que se formen un menor número de grupos los datos están más agrupados.

Sin embargo, en el conjunto de datos de entrenamiento (train) los datos si es verdad que forman un mayor número de grupos pero hay muchos datos que se quedan dispersos por el medio o a medio camino de formar parte de alguno de los grupos, por lo que creo que debería entrenarse un poco más el modelo para que realmente se puedan llegar a formar grupos en condiciones que puedan ser analizados.